﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.IsolatedStorage;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using MainGame.GamePlay;
using Microsoft.Xna.Framework.GamerServices;

namespace MainGame.SaveData
{

  /// <summary>
  /// Information about a single user profile
  /// </summary>
  public struct Profile
  {
    public int id;
    public string username;
    public int gamesPlayed;
    public int gamesWon;
    public int bestScore;
    public double timePlayed;

    public bool HasSavedGames()
    {
      string saveFilePath = Path.Combine("Profiles", Path.Combine("Profile" + id, "SavedGame.car"));
#if WINDOWS || XBOX
      if (File.Exists(saveFilePath))
        return true;
#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      if (appStorage.FileExists(saveFilePath))
        return true;
#endif

      return false;
    }
  }

  /// <summary>
  /// Information about the settings of a single user profile
  /// </summary>
  public struct Configuration
  {
    public float sfxVolume;
    public bool sfxMuted;
    public float musicVolume;
    public bool musicMuted;
    public Backgrounds chosenBackground;
    public Decks chosenDeck;
    public bool tutorial;
    public bool hints;
    public GameDifficulty difficulty;
  }

  /// <summary>
  /// Information about a saved game of a single user profile
  /// </summary>
  public class SavedGame
  {
    public PlayerBlock playerCards;
    public Pack pack;
    public float timeElapsed;
    public int[] mazzetti;

    public bool IsValid()
    {
      // Checks the conditions for a valid saved game
      if (timeElapsed > 0)
        return true;
      return false;
    }
  }

  /// <summary>
  /// A single entry in the high scores chart
  /// </summary>
  public struct ChartEntry
  {
    public string username;
    public int score;
    public double time;
    public GameDifficulty difficulty;
  }

  /// <summary>
  /// The list of entries in the high scores chart
  /// </summary>
  public struct Chart
  {
    public List<ChartEntry> entries;
  }

  /// <summary>
  /// General information useful to the game
  /// (load at game start, save at game end)
  /// </summary>
  public struct DefaultInfo
  {
    public int lastUser;
    public int lastProfileId;
  }

  /// <summary>
  /// Provides functions to access data saved on disk
  /// </summary>
  static public class SaveUtils
  {
    static public string saveDir = "Profiles";
    static public Profile defaultProfile = new Profile { id = 0, username = "default", gamesPlayed = 0, gamesWon = 0, bestScore = 0, timePlayed = 0 }; // id = 0: default user (no profiles)
    static public Configuration defaultConfig = new Configuration { sfxVolume = 1.0f, sfxMuted = false, musicVolume = 1.0f, musicMuted = false, chosenDeck = Decks.ClassicPoker, chosenBackground = Backgrounds.Spiral, tutorial = true, hints = true, difficulty = GameDifficulty.Easy };
    static public SavedGame defaultGame = new SavedGame { playerCards = null, pack = null, timeElapsed = 0.0f, mazzetti = null };
    static public DefaultInfo defaultInfo = new DefaultInfo { lastUser = -1, lastProfileId = -1 };  // -1: it's the first time the game has been launched

    /// <summary>
    /// Saves a user profile
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="profile">The profile to save</param>
    static public void SaveProfile(this Game game, Profile profile)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Profile));
      string saveFile = Path.Combine(saveDir, "Profile" + profile.id + ".car");

#if WINDOWS || XBOX
      if (!Directory.Exists(saveDir))
        // No profiles created yet: create the profiles directory
        Directory.CreateDirectory(saveDir);
      string profileDir = Path.Combine(saveDir, "Profile" + profile.id);
      if (!Directory.Exists(profileDir))
        // The profile is new: create the profile sub-directory
        Directory.CreateDirectory(profileDir);
      StreamWriter writer = new StreamWriter(saveFile);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If this is the first profile being saved, create the profiles directory
      if (!appStorage.DirectoryExists(saveDir))
        appStorage.CreateDirectory(saveDir);
      string profileSubDir = Path.Combine(saveDir, "Profile" + profile.id);
      // If this is the first time this profile is being saved, create the profile directory
      if (!appStorage.DirectoryExists(profileSubDir))
        appStorage.CreateDirectory(profileSubDir);
      // Replace the existing saved profile with the new one
      if (appStorage.FileExists(saveFile))
        appStorage.DeleteFile(saveFile);
      IsolatedStorageFileStream streamFile = appStorage.OpenFile(saveFile, FileMode.OpenOrCreate);
      StreamWriter writer = new StreamWriter(streamFile);

#endif
      serializer.Serialize(writer, profile);
      writer.Close();
    }

    /// <summary>
    /// Loads a user profile
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="username">The id number of the user</param>
    /// <returns>The profile of the user with the given id</returns>
    static public Profile LoadProfile(this Game game, int id)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Profile));
      string saveFile = Path.Combine(saveDir, "Profile" + id + ".car");

#if WINDOWS || XBOX
      // If the requested profile doesn't exists, return default profile
      if (!File.Exists(saveFile))
        return defaultProfile;
      FileStream filestream = new FileStream(saveFile, FileMode.OpenOrCreate);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If the requested profile doesn't exists, return default profile
      if (!appStorage.FileExists(saveFile))
        return defaultProfile;
      IsolatedStorageFileStream filestream = appStorage.OpenFile(saveFile, FileMode.Open, FileAccess.Read);

#endif
      // Deserialize the XML file 
      Profile profile = (Profile)serializer.Deserialize(filestream);
      filestream.Close();
      return profile;
    }


    /// <summary>
    /// Loads a user profile
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="username">The profile string name of the user</param>
    /// <returns>The profile of the user with the given profile string name</returns>
    static public Profile LoadProfileByString(this Game game, string name)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Profile));
      string saveFile = Path.Combine(saveDir, name);

#if WINDOWS || XBOX
      // If the requested profile doesn't exists, return default profile
      if (!File.Exists(saveFile))
        return defaultProfile;
      FileStream filestream = new FileStream(saveFile, FileMode.OpenOrCreate);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If the requested profile doesn't exists, return default profile
      if (!appStorage.FileExists(saveFile))
        return defaultProfile;
      IsolatedStorageFileStream filestream = appStorage.OpenFile(saveFile, FileMode.Open, FileAccess.Read);

#endif
      // Deserialize the XML file 
      Profile profile = (Profile)serializer.Deserialize(filestream);
      filestream.Close();
      return profile;
    }

    /// <summary>
    /// Saves a user configuration
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="config">The configuration to save</param>
    /// <param name="id">The id number of the user to save the configuration for</param>
    static public void SaveConfig(this Game game, Configuration config, int id)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
      string configDir = Path.Combine(saveDir, "Profile" + id);
      string configFile = Path.Combine(configDir, "Config.car");

#if WINDOWS || XBOX
      if (!Directory.Exists(saveDir))
        // No profiles created yet: create the profiles directory
        Directory.CreateDirectory(saveDir);
      if (!Directory.Exists(configDir))
        // The profile is new: create the profile sub-directory
        Directory.CreateDirectory(configDir);
      StreamWriter writer = new StreamWriter(configFile);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If this is the first configuration being saved, create the profiles directory
      if (!appStorage.DirectoryExists(saveDir))
        appStorage.CreateDirectory(saveDir);
      // If this is the first time this profile is being saved, create the profile directory
      if (!appStorage.DirectoryExists(configDir))
        appStorage.CreateDirectory(configDir);
      // Replace the existing saved configuration with the new one
      if (appStorage.FileExists(configFile))
        appStorage.DeleteFile(configFile);
      IsolatedStorageFileStream streamFile = appStorage.OpenFile(configFile, FileMode.OpenOrCreate);
      StreamWriter writer = new StreamWriter(streamFile);

#endif
      serializer.Serialize(writer, config);
      writer.Close();
    }

    /// <summary>
    /// Loads a user configuration
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="id">The id number of the user</param>
    /// <returns>The configuration for the user with the given id</returns>
    static public Configuration LoadConfig(this Game game, int id)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
      string configDir = Path.Combine(saveDir, "Profile" + id);
      string configFile = Path.Combine(configDir, "Config.car");

#if WINDOWS || XBOX
      // If the requested configuration doesn't exists, return default configuration
      if (!Directory.Exists(configDir) || !File.Exists(configFile))
        return defaultConfig;

      FileStream filestream = new FileStream(configFile, FileMode.OpenOrCreate);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If the requested configuration doesn't exists, return default configuration
      if (!appStorage.DirectoryExists(configDir) || !appStorage.FileExists(configFile))
        return defaultConfig;
      IsolatedStorageFileStream filestream = appStorage.OpenFile(configFile, FileMode.Open, FileAccess.Read);

#endif
      // Deserialize the XML file 
      Configuration config = (Configuration)serializer.Deserialize(filestream);
      filestream.Close();
      return config;
    }

    /// <summary>
    /// Saves the game currently played
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="config">The game to save</param>
    /// <param name="id">The id number of the user to save the game for</param>
    static public void SaveGame(this Game game, SavedGame saveGame, int id)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(SavedGame));
      string saveGameDir = Path.Combine(saveDir, "Profile" + id);
      string saveGameFile = Path.Combine(saveGameDir, "SavedGame.car");

#if WINDOWS || XBOX
      if (!Directory.Exists(saveDir))
        // No profiles created yet: create the profiles directory
        Directory.CreateDirectory(saveDir);
      if (!Directory.Exists(saveGameDir))
        // The profile is new: create the profile sub-directory
        Directory.CreateDirectory(saveGameDir);
      StreamWriter writer = new StreamWriter(saveGameFile);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If this is the first game being saved, create the profiles directory
      if (!appStorage.DirectoryExists(saveDir))
        appStorage.CreateDirectory(saveDir);
      // If this is the first time this profile is being saved, create the profile directory
      if (!appStorage.DirectoryExists(saveGameDir))
        appStorage.CreateDirectory(saveGameDir);
      // Replace the existing saved game with the new one
      if (appStorage.FileExists(saveGameFile))
        appStorage.DeleteFile(saveGameFile);
      IsolatedStorageFileStream streamFile = appStorage.OpenFile(saveGameFile, FileMode.OpenOrCreate);
      StreamWriter writer = new StreamWriter(streamFile);

#endif
      serializer.Serialize(writer, saveGame);
      writer.Close();
    }

    /// <summary>
    /// Loads a saved game
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="id">The id number of the user</param>
    /// <returns>The saved game for the user with the given id</returns>
    static public SavedGame LoadGame(this Game game, int id)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(SavedGame));
      string savedGameDir = Path.Combine(saveDir, "Profile" + id);
      string savedGameFile = Path.Combine(savedGameDir, "SavedGame.car");

#if WINDOWS || XBOX
      // If the requested saved game doesn't exists, return default saved game (empty)
      if (!Directory.Exists(savedGameDir) || !File.Exists(savedGameFile))
        return null;
      FileStream filestream = new FileStream(savedGameFile, FileMode.OpenOrCreate);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If the requested saved game doesn't exists, return default saved game (empty)
      if (!appStorage.DirectoryExists(savedGameDir) || !appStorage.FileExists(savedGameFile))
        return null;
      IsolatedStorageFileStream filestream = appStorage.OpenFile(savedGameFile, FileMode.Open, FileAccess.Read);

#endif

      SavedGame savedGame = (SavedGame)serializer.Deserialize(filestream);
      filestream.Close();
      return savedGame;
    }


    /// <summary>
    /// Deletes a saved game
    /// </summary>
    /// <param name="game">The current game instance</param>
    /// <param name="id">The id number of the user to delete the saved game for</param>
    static public void DeleteGame(this Game game, int id)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(SavedGame));
      string saveGameDir = Path.Combine(saveDir, "Profile" + id);
      string saveGameFile = Path.Combine(saveGameDir, "SavedGame.car");

#if WINDOWS || XBOX
      // If the requested saved game doesn't exists, return
      if (!Directory.Exists(saveGameDir) || !File.Exists(saveGameFile))
        return;
      File.Delete(saveGameFile);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If the requested saved game doesn't exists, return
      if (!appStorage.DirectoryExists(saveGameDir) || !appStorage.FileExists(saveGameFile))
        return;
      appStorage.DeleteFile(saveGameFile);

#endif

    }


    /// <summary>
    /// Saves the high scores chart
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="chart">The chart to save</param>
    static public void SaveChart(this Game game, Chart chart)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Chart));
      string saveChartFile = Path.Combine(saveDir, "chart.car");

#if WINDOWS || XBOX
      if (!Directory.Exists(saveDir))
        // No profiles created yet: create the profiles directory
        Directory.CreateDirectory(saveDir);
      StreamWriter writer = new StreamWriter(saveChartFile);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If this is the first information being saved, create the profiles directory
      if (!appStorage.DirectoryExists(saveDir))
        appStorage.CreateDirectory(saveDir);
      // Replace the existing default information with the new one
      if (appStorage.FileExists(saveChartFile))
        appStorage.DeleteFile(saveChartFile);
      IsolatedStorageFileStream streamFile = appStorage.OpenFile(saveChartFile, FileMode.OpenOrCreate);
      StreamWriter writer = new StreamWriter(streamFile);

#endif
      serializer.Serialize(writer, chart);
      writer.Close();
    }


    /// <summary>
    /// Loads the high scores chart
    /// </summary>
    /// <param name="game">The current game</param>
    /// <returns>The saved high scores chart</returns>
    static public Chart LoadChart(this Game game)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(Chart));
      string saveChartFile = Path.Combine(saveDir, "chart.car");

#if WINDOWS || XBOX
      // If there is no chart saved, return an empty chart
      if (!File.Exists(saveChartFile))
        return new Chart() { entries = new List<ChartEntry>(10) };
      FileStream filestream = new FileStream(saveChartFile, FileMode.OpenOrCreate);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If there is no chart saved, return an empty chart
      if (!appStorage.FileExists(saveChartFile))
        return new Chart() { entries = new List<ChartEntry>(10) };
      IsolatedStorageFileStream filestream = appStorage.OpenFile(saveChartFile, FileMode.Open, FileAccess.Read);

#endif

      Chart chart = (Chart)serializer.Deserialize(filestream);
      filestream.Close();
      return chart;
    }


    /// <summary>
    /// Saves defaults for the game
    /// </summary>
    /// <param name="game">The current game</param>
    /// <param name="config">The default information to save</param>
    static public void SaveDefaults(this Game game, DefaultInfo defaults)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(DefaultInfo));
      string saveDefaultsFile = Path.Combine(saveDir, "defaults.car");

#if WINDOWS || XBOX
      if (!Directory.Exists(saveDir))
        // No profiles created yet: create the profiles directory
        Directory.CreateDirectory(saveDir);
      StreamWriter writer = new StreamWriter(saveDefaultsFile);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If this is the first information being saved, create the profiles directory
      if (!appStorage.DirectoryExists(saveDir))
        appStorage.CreateDirectory(saveDir);
      // Replace the existing default information with the new one
      if (appStorage.FileExists(saveDefaultsFile))
        appStorage.DeleteFile(saveDefaultsFile);
      IsolatedStorageFileStream streamFile = appStorage.OpenFile(saveDefaultsFile, FileMode.OpenOrCreate);
      StreamWriter writer = new StreamWriter(streamFile);

#endif
      serializer.Serialize(writer, defaults);
      writer.Close();
    }
    
    /// <summary>
    /// Loads defaults for the game
    /// </summary>
    /// <param name="game">The current game</param>
    /// <returns>The default information for the game</returns>
    static public DefaultInfo LoadDefaults(this Game game)
    {
      XmlSerializer serializer = new XmlSerializer(typeof(DefaultInfo));
      string saveDefaultsFile = Path.Combine(saveDir, "defaults.car");

#if WINDOWS || XBOX
      // If the requested information doesn't exists, return default information
      if (!File.Exists(saveDefaultsFile))
        return defaultInfo;
      FileStream filestream = new FileStream(saveDefaultsFile, FileMode.OpenOrCreate);

#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      // If the requested information doesn't exists, return default information
      if (!appStorage.FileExists(saveDefaultsFile))
        return defaultInfo;
      IsolatedStorageFileStream filestream = appStorage.OpenFile(saveDefaultsFile, FileMode.Open, FileAccess.Read);

#endif

      DefaultInfo info = (DefaultInfo)serializer.Deserialize(filestream);
      filestream.Close();
      return info;

    }

    /// <summary>
    /// Gets the next available user id
    /// </summary>
    /// <returns>The next available user id</returns>
    static public int GetNextAvailableUId(this Game game)
    {
      return game.LoadDefaults().lastProfileId + 1;
    }


    /// <summary>
    /// Gets an user id
    /// </summary>
    /// <returns>The username of the user id</returns>
    static public string GetUsernameById(this Game game, int id)
    {
      return game.LoadProfile(id).username;
    }


    /// <summary>
    /// </summary>
    /// <returns>all avaiable users in a string array</returns>
    static public List<string> GetUsernameList(this Game game)
    {
      List<string> names = new List<string>();
      string[] fileNames = new string[0];

      int nextUserId = game.GetNextAvailableUId();

      for (int i = 0; i < nextUserId; i++)
      {
        var curName = game.GetUsernameById(i);
        if (!curName.Equals("default"))
          names.Add(curName);
      }
    
      return names;
    }

    /// <summary>
    /// Returns a list of the saved profiles
    /// </summary>
    /// <param name="game"></param>
    /// <returns>The list of the saved profiles</returns>
    static public List<string> GetProfileList(this Game game)
    {
      List<string> names = new List<string>();
      string[] fileNames = new string[0];
      try
      {
#if WINDOWS || XBOX
        if (Directory.Exists(saveDir))
          fileNames = Directory.GetFiles(saveDir, "Profile*", SearchOption.TopDirectoryOnly);
#else
        IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
        if (appStorage.DirectoryExists(saveDir))
          fileNames = appStorage.GetFileNames("Profiles\\Profile*");
#endif
      }
      catch (Exception) { return names; }

      List<string> profileNames = new List<string>();

      for (int i = 0; i < fileNames.Count(); ++i)
      {
        if (fileNames[i].Contains("Profile"))
          profileNames.Add(fileNames[i]);
      }

      for (int i = 1; i < profileNames.Count(); i++)
      {
        var curName = profileNames.ElementAt(i);
#if WINDOWS || XBOX
          names.Add(curName.Substring(9));
#else
          names.Add(curName);
#endif
      }
      return names;
    }


    /// <summary>
    /// Delete an user Profile by id
    /// </summary>
    static public void DeleteUserById(int id)
    {
      string pathDir = Path.Combine(saveDir, "Profile" + id);
      string pathFile = Path.Combine(saveDir, "Profile" + id + ".car");

#if WINDOWS || XBOX
      if (Directory.Exists(pathDir) && File.Exists(pathFile))
      {
        Directory.Delete(pathDir, true);
        File.Delete(pathFile);
      }
#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      if (appStorage.DirectoryExists(pathDir) && appStorage.FileExists(pathFile))
      {        
        string[] files = appStorage.GetFileNames(pathDir);

        for(int i = 0; i < files.Count(); i++)
        {
          appStorage.DeleteFile(files[i]);       
        }
        appStorage.DeleteDirectory(pathDir);
        appStorage.DeleteFile(pathFile);
      }
#endif


    }

    /// <summary>
    /// Delete an user Profile by Profile string name
    /// </summary>
    static public void DeleteUserByProfile(string name)
    {
      int nameLen = name.Length;
      nameLen = nameLen - 4;
      string pathDir = Path.Combine(saveDir, name.Substring(0, nameLen));
      string pathFile = Path.Combine(saveDir, name);

#if WINDOWS || XBOX
      if (Directory.Exists(pathDir) && File.Exists(pathFile))
      {
        Directory.Delete(pathDir, true);
        File.Delete(pathFile);
      }
#else
      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
      if (appStorage.DirectoryExists(pathDir) && appStorage.FileExists(pathFile))
      {
        string[] files = appStorage.GetFileNames(pathDir + "\\");

        for (int i = 0; i < files.Count(); i++)
        {
          appStorage.DeleteFile(pathDir + "\\" + files[i]);
        }

        appStorage.DeleteDirectory(pathDir);
        appStorage.DeleteFile(pathFile);
      }
#endif

    }

//    static public void DeleteAllUsers(Game game)
//    {
     
//      List<string> Profiles = GetProfileList(game);

//      for (int i = 0; i < Profiles.Count; i++)
//      {
//        int nameLen = Profiles[i].Length - 4;
//        string pathDir = Path.Combine(saveDir, Profiles[i].Substring(0, nameLen));
//        string pathFile = Path.Combine(saveDir, Profiles[i]);

//#if WINDOWS || XBOX
//        if (Directory.Exists(pathDir) && File.Exists(pathFile))
//        {
//          Directory.Delete(pathDir, true);
//          File.Delete(pathFile);
//        }
//#else
//      IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
//      if (appStorage.DirectoryExists(pathDir) && appStorage.FileExists(pathFile))
//      {        
//        string[] files = appStorage.GetFileNames(pathDir);

//        for(int i = 0; i < files.Count(); i++)
//        {
//          appStorage.DeleteFile(files[i]);       
//        }
//        appStorage.DeleteDirectory(pathDir);
//        appStorage.DeleteFile(pathFile);
//#endif

//      }


//      }



  }
}
